﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace tactics
{
    using Coord = Utility.Pair<int, int>;

    public class TerrainMap
    {
        int width;
        int height;
        float scale;

        bool smooth;

        TileSet tileSet;

        TerrainRule[] ruleMap;
        int[] playerMap;
        HeightMap heightMap;
        TileInfo[] tileMap;

        // for ray vs terrain collision only
        Nuclex.HeightField heightField;

        #region Properties

        public TerrainRule[] Map
        {
            get { return ruleMap; }
        }

        public HeightMap HeightMap
        {
            get { return heightMap; }
        }

        public int Width
        {
            get { return width; }
        }

        public int Height
        {
            get { return height; }
        }

        public float Scale
        {
            get { return scale; }
        }

        public string Effect
        {
            get { return tileSet.Effect; }
        }

        public string Name
        {
            get { return tileSet.Name; }
        }

        public string TileSetTexture
        {
            get { return tileSet.Texture; }
        }

        public int TerrainGridWidth
        {
            get { return tileSet.GridWidth; }
        }

        public List<string> TileTypes
        {
            get { return tileSet.TileTypes; }
        }

        public bool SmoothTerrain
        {
            get { return smooth; }
        }

        public Nuclex.HeightField NuclexHeightField
        {
            get { return heightField; }
        }

        #endregion

        public TerrainMap(MapBuilders.IMapBuilder builder)
        {
            width = builder.Width;
            height = builder.Height;
            scale = builder.Scale;
            
            int length = width * height;
            ruleMap = new TerrainRule[length];
            playerMap = new int[length];

            tileSet = new TileSet(builder.TileXMLFile);

            heightMap = builder.GenerateMap(tileSet);
            tileMap = builder.MapInfo;

            for (int i = 0; i < ruleMap.Length; ++i)
            {
                ruleMap[i] = builder.MapInfo[i].MovementRule;
                playerMap[i] = -1;
            }

            smooth = builder.SmoothTerrain;

            UpdateNuclexField();
        }

        #region Accessors

        public TerrainRule GetTileTerrainRule(string tile)
        {
            return tileSet.GetTileMovementRule(tile);
        }

        public TerrainRule GetTypeAt(int x, int y)
        {
            return ruleMap[AtCoord(x, y)];
        }

        public Vector2[] GetTexCoordsAt(int x, int y)
        {
            TileInfo tile = tileMap[AtCoord(x, y)];
            return tile.TexCoords;
        }

        public int GetPlayerAt(int x, int y)
        {
            return playerMap[AtCoord(x, y)];
        }

        public int GetPlayerAt(Coord pos)
        {
            return playerMap[AtCoord(pos)];
        }

        public int AtCoord(int x, int y)
        {
            return heightMap.AtCoord(x, y);
        }

        public int AtCoord(Coord pos)
        {
            return heightMap.AtCoord(pos.First, pos.Second);
        }

        public float GetAverageHeight(int x, int y)
        {
            int counter = 1;
            float y1 = heightMap.GetHeight(x, y);
            float y2 = 0f;
            float y3 = 0f;
            float y4 = 0f;

            if (x + 1 < width && y + 1 < height)
            {
                y2 = heightMap.GetHeight(x + 1, y);
                y4 = heightMap.GetHeight(x + 1, y + 1);
                y3 = heightMap.GetHeight(x, y + 1);
                counter = 4;
            }

            return (y1 + y2 + y3 + y4) / counter;
        }

        public float GetHeight(int x, int y)
        {
            return heightMap.GetHeight(x, y);
        }

        public Coord GetCoordFromIndex(int idx)
        {
            int x = idx % width;
            int y = idx / width;
            return new Coord(x, y);
        }

        #endregion

        #region Update

        private void UpdateNuclexField()
        {
            heightField = new Nuclex.HeightField(width, height);
            for (int y = 0; y < height; ++y)
                for (int x = 0; x < width; ++x)
                    heightField[x, y] = GetHeight(x, y);
        }

        public void PlayerMapUpdate(List<Player> players)
        {
            playerMap = new int[width * height];

            for (int i = 0; i < playerMap.Length; ++i)
                playerMap[i] = -1;

            foreach (Player p in players)
            {
                foreach (Unit u in p.Units)
                {
                    playerMap[AtCoord(new Coord(u.X, u.Y))] = p.Number;
                }
            }
        }

        #endregion
    }
}
